#include <stdio.h>
#include <string.h>
#include "cpolist.h"
#include "cpodata.h"
#include "es_print.h"
#include "cpotimer.h"
#include "cpomqtt.h"
#include <json-c/json_util.h>
#include <json-c/json.h>
#include <unistd.h>

extern struct single_point syspoint_hd;

extern cpo_dev_t *cpo_dev;
extern struct mosquitto *mqtt_cpo;

/* 添加状态点位到链表
 * @pp: 点位属性指针，指向设备描述符中的某个点位
 * @hd: 链表头
 */
void add_sp_2slist(point_prop_t *pp, struct list_head *hd)
{  
    if(pp == NULL || hd == NULL){
        return;
    }

    struct single_point *sp_pos;
    /* 判重 */
    list_for_each_entry(sp_pos, hd, slist){
        if(strcmp(sp_pos->pp.dev_key, pp->dev_key) == 0 &&
            strcmp(sp_pos->pp.func, pp->func) == 0){
                ES_PRT_ERROR("node has existed: name = %s, deviceKey = %s, func = %s \n", \
                                    pp->name, pp->dev_key, pp->func);
                return;
            }
    }

    struct single_point *sp = malloc(sizeof(struct single_point)*1);
    memcpy(&sp->pp, pp, sizeof(point_prop_t));

    /* add 点位 to list */
    list_add_tail(&sp->slist, hd);

#if 0 //debug
    struct single_point *pos;
    list_for_each_entry(pos, &syspoint_hd.slist, slist){
        printf("name = %s, dk = %s, func = %s, val = %d \n", pos->pp.name, pos->pp.dev_key, pos->pp.func, pos->pp.val.b);
    }
#endif
}

/* 添加开关点位到链表 */
void add_sp_2clist(point_prop_t *pp, struct list_head *hd)
{
    if(pp == NULL || hd == NULL){
        return;
    }

    struct single_point *sp_pos;
    /* 判重 */
    list_for_each_entry(sp_pos, hd, clist){
        if(strcmp(sp_pos->pp.dev_key, pp->dev_key) == 0 &&
            strcmp(sp_pos->pp.func, pp->func) == 0){
                ES_PRT_ERROR("node has existed: name = %s, deviceKey = %s, func = %s \n", \
                                pp->name, pp->dev_key, pp->func);
                return;
            }
    }

    struct single_point *sp = malloc(sizeof(struct single_point)*1);
    memcpy(&sp->pp, pp, sizeof(point_prop_t));

    /* add 点位 to list, 先插的命令点位, 在关闭时是后关闭的 */
    list_add(&sp->clist, hd);
//    list_add_tail(&sp->clist, hd);
}

/* 组装write命令：
{
    "deviceKey" : "@dk",
    "cmd":"write",
    "function" :
    {
        "@chan": @val,
    }
}
 */
void combine_wr_cmd_bypp(struct json_object *jroot, point_prop_t *pp)
{
    struct json_object *jdk = json_object_new_string(pp->dev_key);
    json_object_object_add(jroot, "deviceKey", jdk);

    struct json_object *jcmd = json_object_new_string("write");
    json_object_object_add(jroot, "cmd", jcmd);

    struct json_object *jfunc = json_object_new_object();
    struct json_object *jval;

    if(pp->tag == TypeOfVal_BOOL){
        jval = json_object_new_boolean(pp->val.b);
    }else if(pp->tag == TypeOfVal_DOUBLE){
        jval = json_object_new_double(pp->val.d);
    }else if(pp->tag == TypeOfVal_INT){
        jval = json_object_new_int(pp->val.i);
    }else if(pp->tag == TypeOfVal_STRING){
        jval = json_object_new_string(pp->val.s);
    }

    json_object_object_add(jfunc, pp->func, jval);

    json_object_object_add(jroot, "function", jfunc);
}

/* 将点位的deviceKey、function、value组装成json报文通过mqtt发送出去
 * @name: 点位name
 * @dev_prop: 点位指针
 */
void sendpoint_2otherapp(point_prop_t *pp)
{
    /* 组装json报文 */
    struct json_object *jroot = json_object_new_object();
    combine_wr_cmd_bypp(jroot, pp);
    const char *tx_buf = json_object_to_json_string_ext(jroot, JSON_C_TO_STRING_PRETTY);

    /* mqtt send */
    char topic[120];
    sprintf(topic, "%s/%s/command", MQTT_PUB_PREFIX, pp->dev_key);
    mosquitto_publish(mqtt_cpo, NULL, topic, strlen(tx_buf), tx_buf, 0, 0);

    printf("CPO: send json to %s:\n %s \n", topic, tx_buf);

    json_object_put(jroot);
}

/* 给设备描述符中的点位赋值
 * @name: 点位name
 * @dev_prop: 设备属性指针
 * @val: 要写入的值
 * @tp: val的类型
 */
void setval_2devdp_byname(char *name, dev_prop_t *dev_prop, data_type_u val, enum type_of_val tp)
{
    /* 根据名字找到点位 */
    point_prop_t *pp = is_point_exist(name, dev_prop);
    if(pp == NULL){
        return;
    }

    /* TODO: 没上锁会不会有问题 !!! */

    switch(tp){
        case TypeOfVal_BOOL:
            pp->tag = TypeOfVal_BOOL;
            pp->val.b = val.b;
            break;

        case TypeOfVal_INT:
            pp->tag = TypeOfVal_INT;
            pp->val.i = val.i;
            break;

        case TypeOfVal_DOUBLE:
            pp->tag = TypeOfVal_DOUBLE;
            pp->val.d = val.d;
            break;
        case TypeOfVal_STRING:
            pp->tag = TypeOfVal_STRING;
            strcpy(pp->val.s, val.s);
            break;

        default:
            break;
    }
}

/* 给点位赋值
 * @pp: 点位属性
 * @val: 要写入的值
 * @tp: val的类型
 */
void setval_2devdp_bypp(point_prop_t *pp, data_type_u val, enum type_of_val tp)
{
    switch(tp){
    case TypeOfVal_BOOL:
        pp->val.b = val.b;
        break;

    case TypeOfVal_INT:
        pp->val.i = val.i;
        break;

    case TypeOfVal_DOUBLE:
        pp->val.d = val.d;
        break;
    case TypeOfVal_STRING:
        strcpy(pp->val.s, val.s);
        break;

    default:
        break;
    }

    pp->tag = tp;

}

/* 根据名字读系统虚点的值 */
enum error_code read_sysvp_byname(char *name, data_type_u *val)
{
    int i;

    for(i=0; i<cpo_dev->sys_vp.points_num; i++){
        if(strcmp(name, cpo_dev->sys_vp.points[i].name) == 0){
            break;
        }
    }
    
    if(i == cpo_dev->sys_vp.points_num){
        ES_PRT_WARN("'%s' you want to read value unexist \n", name);
        return ErrNoExist;
    }

    *val = cpo_dev->sys_vp.points[i].val;
    return Success;
}

/* 根据名字读系统实点的值 */
enum error_code read_sysrp_byname(char *name, data_type_u *val)
{
    int i;

    for(i=0; i<cpo_dev->sys_rp.points_num; i++){
        if(strcmp(name, cpo_dev->sys_rp.points[i].name) == 0){
            break;
        }
    }

    if(i == cpo_dev->sys_rp.points_num){
        //ES_PRT_WARN("'%s' you want to read value unexist \n", name);
        return ErrNoExist;
    }

    *val = cpo_dev->sys_rp.points[i].val;
    return Success;
}

/* 判断点位name是否存在
 * 存在就返回 点位地址
 * 不存在就返回 NULL
 */
point_prop_t *is_point_exist(char *name, dev_prop_t *dev_prop)
{
    int i;
    for(i=0; i<dev_prop->points_num; i++){
        if(strcmp(name, dev_prop->points[i].name) == 0){
            return dev_prop->points+i;
        }
    }

    return NULL;
}

/* 根据deviceKey和function(key)找到点位(从整个冷源设备描述符中查找)
 * return: 单个点位的属性描述符
 */
point_prop_t *find_point_bydf(char *dk, char *key)
{
    if(dk == NULL || key == NULL){
        return NULL;
    }

    int i;
    // 1. 冷源系统实点+虚点
    for(i=0; i<cpo_dev->sys_rp.points_num; i++){
        if(strcmp(key, cpo_dev->sys_rp.points[i].func) == 0 
            && strcmp(dk, cpo_dev->sys_rp.points[i].dev_key) == 0){
            return cpo_dev->sys_rp.points+i;
        }
    }
    for(i=0; i<cpo_dev->sys_vp.points_num; i++){
        if(strcmp(key, cpo_dev->sys_vp.points[i].func) == 0 
            && strcmp(dk, cpo_dev->sys_vp.points[i].dev_key) == 0){
            return cpo_dev->sys_vp.points+i;
        }
    }
    // 2. 每个初级冷却水泵实点
    wp_dev_t *wp_pos;
    list_for_each_entry(wp_pos, &cpo_dev->pchwp_head.list, list){
        for(i=0; i<wp_pos->wp_prop.points_num; i++){
            if(strcmp(key, wp_pos->wp_prop.points[i].func) == 0 
                && strcmp(dk, wp_pos->wp_prop.points[i].dev_key) == 0){
                return wp_pos->wp_prop.points+i;
            }
        }
    }
    // 3. 每个冷却水泵实点
    list_for_each_entry(wp_pos, &cpo_dev->cwp_head.list, list){
        for(i=0; i<wp_pos->wp_prop.points_num; i++){
            if(strcmp(key, wp_pos->wp_prop.points[i].func) == 0 
                && strcmp(dk, wp_pos->wp_prop.points[i].dev_key) == 0){
                return wp_pos->wp_prop.points+i;
            }
        }
    }
    // 4. 冷机的实点
    ch_dev_t *ch_pos;
    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){
        for(i=0; i<ch_pos->ch_prop.points_num; i++){
            if(strcmp(key, ch_pos->ch_prop.points[i].func) == 0 
                && strcmp(dk, ch_pos->ch_prop.points[i].dev_key) == 0){
                return ch_pos->ch_prop.points+i;
            }
        }
    }
    // 冷机组实点
    for(i=0; i<cpo_dev->ch_head.chg_rp.points_num; i++){
        if(strcmp(key, cpo_dev->ch_head.chg_rp.points[i].func) == 0 
            && strcmp(dk, cpo_dev->ch_head.chg_rp.points[i].dev_key) == 0){
            return cpo_dev->ch_head.chg_rp.points+i;
        }
    }
    // 5. 冷却塔实点
    ct_dev_t *ct_pos;
    ctf_dev_t *ctf_pos;
    list_for_each_entry(ct_pos, &cpo_dev->ct_head.list, list_ct){
        for(i=0; i<ct_pos->ct_prop.points_num; i++){
            if(strcmp(key, ct_pos->ct_prop.points[i].func) == 0 
                && strcmp(dk, ct_pos->ct_prop.points[i].dev_key) == 0){
                return ct_pos->ct_prop.points+i;
            }
        }
        // 风扇实点
        list_for_each_entry(ctf_pos, &ct_pos->list_ctf_hd, list){
            for(i=0; i<ctf_pos->ctf_prop.points_num; i++){
                if(strcmp(key, ctf_pos->ctf_prop.points[i].func) == 0 
                    && strcmp(dk, ctf_pos->ctf_prop.points[i].dev_key) == 0){
                    return ctf_pos->ctf_prop.points+i;
                }
            }
        }
    }
    // 冷却塔组实点
    for(i=0; i<cpo_dev->ct_head.ctg_rp.points_num; i++){
        if(strcmp(key, cpo_dev->ct_head.ctg_rp.points[i].func) == 0 
            && strcmp(dk, cpo_dev->ct_head.ctg_rp.points[i].dev_key) == 0){
            return cpo_dev->ct_head.ctg_rp.points+i;
        }
    }

    return NULL;
}

/* 根据点位name找到点位(从整个冷源设备描述符中查找) 
 * NOTE: 慎用这个函数, 因为两个冷却水泵的状态点位的name是一样的
 *       只根据name找到的点位不一定是你想要的,
 *       所以这个函数只适合查找一些全局的name唯一的点位
 * 
 * return: 单个点位的属性描述符
 */
point_prop_t *find_point_bynm(char *name)
{
    int i;
    // 1. 冷源系统实点+虚点
    for(i=0; i<cpo_dev->sys_rp.points_num; i++){
        if(strcmp(name, cpo_dev->sys_rp.points[i].name) == 0){
            return cpo_dev->sys_rp.points+i;
        }
    }

    for(i=0; i<cpo_dev->sys_vp.points_num; i++){
        if(strcmp(name, cpo_dev->sys_vp.points[i].name) == 0){
            return cpo_dev->sys_vp.points+i;
        }
    }
    // 2. 每个初级冷冻水泵实点
    wp_dev_t *wp_pos;
    list_for_each_entry(wp_pos, &cpo_dev->pchwp_head.list, list){
        for(i=0; i<wp_pos->wp_prop.points_num; i++){
            if(strcmp(name, wp_pos->wp_prop.points[i].name) == 0){
                return wp_pos->wp_prop.points+i;
            }
        }
    }
    // 3. 每个冷却水泵实点
    list_for_each_entry(wp_pos, &cpo_dev->cwp_head.list, list){
        for(i=0; i<wp_pos->wp_prop.points_num; i++){
            if(strcmp(name, wp_pos->wp_prop.points[i].name) == 0){
                return wp_pos->wp_prop.points+i;
            }
        }
    }
    // 4. 冷机的实点
    ch_dev_t *ch_pos;
    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){
        for(i=0; i<ch_pos->ch_prop.points_num; i++){
            if(strcmp(name, ch_pos->ch_prop.points[i].name) == 0){
                return ch_pos->ch_prop.points+i;
            }
        }
    }
    // 冷机组实点
    for(i=0; i<cpo_dev->ch_head.chg_rp.points_num; i++){
        if(strcmp(name, cpo_dev->ch_head.chg_rp.points[i].name) == 0){
            return cpo_dev->ch_head.chg_rp.points+i;
        }
    }
    // 5. 冷却塔实点
    ct_dev_t *ct_pos;
    ctf_dev_t *ctf_pos;
    list_for_each_entry(ct_pos, &cpo_dev->ct_head.list, list_ct){
        for(i=0; i<ct_pos->ct_prop.points_num; i++){
            if(strcmp(name, ct_pos->ct_prop.points[i].name) == 0){
                return ct_pos->ct_prop.points+i;
            }
        }
        // 风扇实点
        list_for_each_entry(ctf_pos, &ct_pos->list_ctf_hd, list){
            for(i=0; i<ctf_pos->ctf_prop.points_num; i++){
                if(strcmp(name, ctf_pos->ctf_prop.points[i].name) == 0){
                    return ctf_pos->ctf_prop.points+i;
                }
            }
        }
    }
    // 冷却塔组实点
    for(i=0; i<cpo_dev->ct_head.ctg_rp.points_num; i++){
        if(strcmp(name, cpo_dev->ct_head.ctg_rp.points[i].name) == 0){
            return cpo_dev->ct_head.ctg_rp.points+i;
        }
    }

    return NULL;
}

/* 根据点位name从单个设备的描述符找到点位
 * @name: 点位name
 * @dev_prop: 点位所在设备的设备属性描述符 
 * return: 单个点位的属性描述符
 */
point_prop_t *find_point_from_devdp_bynm(char *name, dev_prop_t *dev_prop)
{
    int i;
    for(i=0; i<dev_prop->points_num; i++){
        if(strcmp(name, dev_prop->points[i].name) == 0){
            return dev_prop->points+i;
        }
    }
    return NULL;
}

/* 如果点位存在，设置点位的在设备描述符中的值，并组装json发送给其他app
 * @name: 点位name
 * @dev_prop: 点位所在设备的设备描述符
 * @val: 要设置的value
 * @tp: tp的类型
 * @hd: 将点位加到哪个链表
 *      NULL: 不把点位加到状态机的某链表中 
 */
void set_cmdpoint_ifexist(char *name, dev_prop_t *dev_prop, data_type_u val, enum type_of_val tp, struct list_head *hd)
{
    point_prop_t *pp;
    pp = find_point_from_devdp_bynm(name, dev_prop);

    if(pp != NULL){
        ES_PRT_INFO("CPO: %s exist, set val \n", name);
        setval_2devdp_bypp(pp, val, tp);
        sendpoint_2otherapp(pp);
        if(hd != NULL){
            add_sp_2clist(pp, hd);
        }
    }
}

/* 处理信号 */
void sigroutine(int signo)
{
    if(signo == SIGPROF){
        es_timer_del(TMR_DELDEVICE);
    }
}

/* 更新设备描述符在运行阶段相关的属性 */
void upd_devdp_runtime()
{
    point_prop_t *pp;
    int ch_cnt = 0;
    int pchwp_cnt = 0;
    int cwp_cnt = 0;

    /* 更新每一个水泵 */
    /* 1. 冷冻水泵 */
    wp_dev_t *pchwp_pos;

    list_for_each_entry(pchwp_pos, &cpo_dev->pchwp_head.list, list){
        pp = find_point_from_devdp_bynm("PCHWP-S", &pchwp_pos->wp_prop);
        /* 状态点位不存在的话就不统计运行时间了 */
        if(pp == NULL){
            return;
        }
        if(pp->val.b == true){
            pchwp_pos->wp_prop.is_run = RUNING;
            pchwp_cnt++;
            pchwp_pos->wp_prop.curr_rt += 1;
            pchwp_pos->wp_prop.accl_rt += 1;
        }else{
            pchwp_pos->wp_prop.is_run = !RUNING;
            pchwp_pos->wp_prop.curr_rt = 0;
        }
    }
    cpo_dev->pchwp_head.pchwp_on_cnt = pchwp_cnt;

    /* 2. 冷却水泵 */
    wp_dev_t *cwp_pos;
    list_for_each_entry(cwp_pos, &cpo_dev->cwp_head.list, list){
        pp = find_point_from_devdp_bynm("CWP-S", &cwp_pos->wp_prop);
        /* 状态点位不存在的话就不统计运行时间了 */
        if(pp == NULL){
            return;
        }
        if(pp->val.b == true){
            cwp_pos->wp_prop.is_run = RUNING;
            cwp_cnt++;
            cwp_pos->wp_prop.curr_rt += 1;
            cwp_pos->wp_prop.accl_rt += 1;
        }else{
            cwp_pos->wp_prop.is_run = !RUNING;
            cwp_pos->wp_prop.curr_rt = 0;
        }
    }
    cpo_dev->cwp_head.cwp_on_cnt = cwp_cnt;

    ch_dev_t *ch_pos;
    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){
        pp = find_point_from_devdp_bynm("CH-S", &ch_pos->ch_prop);
        /* 状态点位不存在的话就没法确定运行状态, 所以我认为状态点位应该存在 */
        if(pp == NULL){
            return;
        }
        if(pp->val.b == true){
            ch_pos->ch_prop.is_run = RUNING;
            ch_cnt++;
            ch_pos->ch_prop.curr_rt += 1;
            ch_pos->ch_prop.accl_rt += 1;
        }else{
            ch_pos->ch_prop.is_run = !RUNING;
            ch_pos->ch_prop.curr_rt = 0;
        }
    }
    cpo_dev->ch_head.ch_on_cnt = ch_cnt;

}

/* 更新冷机设定温度 */
void upd_set_temp()
{
    point_prop_t *pp_v;
    pp_v = find_point_from_devdp_bynm("CHW-TSP-V", &cpo_dev->sys_vp);

    ch_dev_t *ch_pos;
    point_prop_t *pp;

    /* 遍历冷机 */
    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){
        /* 确认冷机运行起来, [要求状态点位必须存在！！！] */
        pp = find_point_from_devdp_bynm("CH-S", &ch_pos->ch_prop);
//        if(pp->val.b != RUNING){
//            continue;
//        }

        pp = find_point_from_devdp_bynm("CHW-TSP", &ch_pos->ch_prop);

        /* 如果点位存在 */
        if(pp != NULL){
            if( (pp->val.d - pp_v->val.d) < 0.1 && (pp->val.d - pp_v->val.d) > -0.1){
                continue;
            }

            /* 温度预设值 和 冷机实际预设值 不匹配就更新冷机 */
            pp->val.d = pp_v->val.d;
            pp->tag = TypeOfVal_DOUBLE;
            sendpoint_2otherapp(pp);
        }
    }

}

/* 更新运行状态有关的属性 */
void *cpo_updprop_thread(void *arg)
{
    while(1){
        sleep(1);
        /* 等待有设备被添加 */
        if(cpo_dev->add_dev == false){
            continue;
        }
        upd_devdp_runtime();
        upd_set_temp();
    }
}
